package com.mic.config;

import com.alibaba.fastjson2.JSON;
import com.mic.authentication.OpenAuthPasswordAuthenticationConverter;
import com.mic.authentication.OpenAuthPasswordAuthenticationProvider;
import com.mic.authentication.PhoneCodeAuthenticationConverter;
import com.mic.authentication.PhoneCodeAuthenticationProvider;
import com.mic.entity.AjaxResult;
import com.mic.handler.AuthenticationEntryPointImpl;
import com.mic.handler.LoginSuccessHandler;
import com.mic.util.RSAUtil;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.token.*;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;


import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.List;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * @author cch
 * @date 2025年05月30日 9:25
 */
@Configuration
public class AuthorizationServerConfig {


    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, OAuth2AuthorizationService authorizationService,
                                                               OAuth2TokenGenerator<?> tokenGenerator, AuthenticationManager authenticationManager) throws Exception{

        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                // oidc配置
                .oidc(withDefaults())
                // 加入自定义的PhoneCodeAuthenticationConverter和PhoneCodeAuthenticationProvider
                .tokenEndpoint(tokenEndpoint ->
                    tokenEndpoint
                            .accessTokenRequestConverters(converters -> converters.addAll(List.of(new PhoneCodeAuthenticationConverter(), new OpenAuthPasswordAuthenticationConverter())))
                            .authenticationProviders( providers -> providers.addAll(List.of(new PhoneCodeAuthenticationProvider(authorizationService, tokenGenerator),
                                    new OpenAuthPasswordAuthenticationProvider(authenticationManager, authorizationService, tokenGenerator))))
                )
        ;
        // 资源服务器默认jwt配置
        http.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(withDefaults()));
        // 异常处理
        http.exceptionHandling((exceptions) -> exceptions.authenticationEntryPoint(
                new AuthenticationEntryPointImpl()
               )
        );
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);
        return providerManager;
    }

    /**
     * 不走过滤器链的放行配置
     */
//    @Bean
//    public WebSecurityCustomizer webSecurityCustomizer() {
//        return (web) -> web.ignoring()
//                .requestMatchers(
//                        "/oauth2/authorize"
//                );
//    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.builder()
                .username("admin")
                .password("$2a$10$uY2crhlbvqfVuVSzzUAJ0OGf2QBAWgQ1P3F.lLYpWbubkBLyQG0Na")
                .roles("ADMIN")
                .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

    public static void main(String[] args) {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encode = bCryptPasswordEncoder.encode("admin");
        boolean admin = bCryptPasswordEncoder.matches("secret", "$2a$10$CU9EPicFGzWxnqyQkDREUO3bslVNOM.E.KKsBdedYz8mylNhsG9ea");
        System.out.println(admin);
    }

    @Bean
    @Order(SecurityProperties.BASIC_AUTH_ORDER)
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
            .formLogin(from -> from.failureHandler((request, response, exception) -> {
                int code = HttpStatus.UNAUTHORIZED.value();
                response.setStatus(200);
                response.setContentType("application/json");
                response.setCharacterEncoding("utf-8");
                response.getWriter().print(JSON.toJSONString(AjaxResult.error(code, "请求访问111：" + request.getRequestURI() + "，认证失败，" + exception.getMessage())));
            }).successHandler(loginSuccessHandler())
            )
            .csrf(csrf -> csrf.disable());
        return http.build();
    }

    @Bean
    public AuthenticationSuccessHandler loginSuccessHandler() {
        return new LoginSuccessHandler();
    }

    //不配置PasswordEncoder，密文前边需要添加前缀{bcrypt}，表明使用的加密方式，
    // 配置了以后无需添加前坠，自动使用你配置的实现类进行解密匹配
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 获取RSA公钥和私钥
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    @Bean
    public JWKSource<SecurityContext> jwkSource(){
        JWKSet jwkSet = new JWKSet(RSAUtil.generateRsa());
        return new ImmutableJWKSet<>(jwkSet);
    }


    /**
     * 自定义OAuth2TokenGenerator，模仿默认配置里面加载3个token生成器
     */
    @Bean
    public OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator() {
        JwtGenerator jwtGenerator = null;
        JwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource());
        if (jwtEncoder != null) {
            jwtGenerator = new JwtGenerator(jwtEncoder);
        }
        OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
        OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
        OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator,
                refreshTokenGenerator);
        return tokenGenerator;
    }

    //生成jks，在命令行模式下，执行下面语句,security要求必须是2048
    //keytool -genkeypair -alias demo -keyalg RSA -keypass linmoo -storepass linmoo -keysize 2048 -keystore demo.jks
//    @Bean
//    public JWKSource<SecurityContext> jwkSource() {
//        KeyPair keyPair = generateRsaKey();
//        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//        RSAKey rsaKey = new RSAKey.Builder(publicKey)
//                .privateKey(privateKey)
//                .keyID(UUID.randomUUID().toString())
//                .build();
//        JWKSet jwkSet = new JWKSet(rsaKey);
//        return new ImmutableJWKSet<>(jwkSet);
//    }

//    /**
//     * 其 key 在启动时生成，用于创建上述 JWKSource
//     */
//    private static KeyPair generateRsaKey() {
//        KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("demo.jks"), "linmoo".toCharArray());
//        KeyPair keyPair = factory.getKeyPair("demo", "linmoo".toCharArray());
//        return keyPair;
//    }

    //数据mysql版本5.7.22，按照oauth2提供的表结构创建数据表，client_secret_expires_at（timestamp）字段不能为null，sql语句会包含，但没有设置具体数值，
    //数据库不能为null会自动转化为当前时间，导致client创建后直接过期不可用，所以重写

//    @Bean
//    public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate){
//        JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
//        return jdbcRegisteredClientRepository;
//    }

//    @Bean
//    public OAuth2AuthorizationService oAuth2AuthorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository){
//        return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
//    }
//
//    @Bean
//    public OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository){
//        return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
//    }

}
